using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

using SlimDX;
using SlimDX.Direct3D9;

namespace miciv.Rendering
{
    public class IndexBufferManager<T> : IManagedResource where T : struct
    {
        protected CoreContext m_coreContext;
        protected IndexBuffer m_indexBuffer;
        protected int m_sIndexCount;
        protected List<ManagedIndexBuffer<T>> m_lstManagedBuffers;
        protected bool m_bIsDirty;
        protected BlocAllocationManager m_allocManager;
        protected int m_sISize;
        protected bool m_bForceUpdate;

        public IndexBuffer IndexBuffer
        {
            get
            {
                return this.m_indexBuffer;
            }
        }

        public bool IsDirty
        {
            get
            {
                return this.m_bIsDirty;
            }
            set
            {
                this.m_bIsDirty = value;
            }
        }

        public IndexBufferManager()
        {
            this.m_lstManagedBuffers = new List<ManagedIndexBuffer<T>>();
            this.m_allocManager = new BlocAllocationManager();
            this.m_sISize = Marshal.SizeOf(typeof(T));
        }

        public bool Initialize(CoreContext _coreContext, int _IndexCount)
        {
            this.m_coreContext = _coreContext;
            this.m_coreContext.GetData<ManagedResourceManager>("ManagedResourceManager").Add(this);
            this.m_sIndexCount = _IndexCount;
            this.m_allocManager.Initialize(_IndexCount);
            this.m_bForceUpdate = false;
            this.DeviceReset();
            return true;
        }

        public ManagedIndexBuffer<T> Create(int _sIndexCount)
        {
            if (false != this.m_allocManager.CanAlloc(_sIndexCount))
            {
                ManagedIndexBuffer<T> mIB = new ManagedIndexBuffer<T>(this);
                mIB.Offset = this.m_allocManager.Alloc(_sIndexCount);
                mIB.IndexCount = _sIndexCount;
                this.m_lstManagedBuffers.Add(mIB);
                return mIB;
            }
            else if (false != this.m_allocManager.CanRealloc(_sIndexCount))
            {
                this.m_allocManager.FreeAll();
                foreach (ManagedIndexBuffer<T> mIB in this.m_lstManagedBuffers)
                {
                    mIB.Offset = this.m_allocManager.Alloc(mIB.IndexCount);
                }
                {
                    ManagedIndexBuffer<T> mIB = new ManagedIndexBuffer<T>(this);
                    mIB.Offset = this.m_allocManager.Alloc(_sIndexCount);
                    mIB.IndexCount = _sIndexCount;
                    this.m_lstManagedBuffers.Add(mIB);
                    return mIB;
                }
            }
            return null;
        }

        public void Release(ManagedIndexBuffer<T> _mIB)
        {
            if (false != this.m_lstManagedBuffers.Contains(_mIB))
            {
                this.m_lstManagedBuffers.Remove(_mIB);
                this.m_allocManager.Free(_mIB.Offset);
            }
        }

        public void Update()
        {
            if (false != this.m_bIsDirty)
            {
                DataStream stream = this.m_indexBuffer.Lock(0, 0, LockFlags.None);
                foreach (ManagedIndexBuffer<T> mIB in this.m_lstManagedBuffers)
                {
                    if ((false != mIB.IsDirty) || (false != this.m_bForceUpdate))
                    {
                        stream.Position = mIB.Offset * this.m_sISize;
                        stream.WriteRange(mIB.Indices);
                        mIB.IsDirty = false;
                    }
                }
                this.m_indexBuffer.Unlock();
                this.m_bIsDirty = false;
                this.m_bForceUpdate = false;
            }
        }

        #region IManagedResource Membres

        public void DeviceLost()
        {
            this.m_indexBuffer.Dispose();
            this.m_indexBuffer = null;
            this.m_bIsDirty = true;
            this.m_bForceUpdate = true;
        }

        public void DeviceReset()
        {
            this.m_indexBuffer = new IndexBuffer(
                this.m_coreContext.GetData<SlimDX.Direct3D9.Device>("D3DDevice"),
                this.m_sIndexCount * this.m_sISize,
                Usage.WriteOnly,
                Pool.Managed,
                (2 == this.m_sISize));
        }

        #endregion
    }
}
